/*

This file is part of Ext JS 4

Copyright (c) 2011 Sencha Inc

Contact:  http://www.sencha.com/contact

Commercial Usage
Licensees holding valid commercial licenses may use this file in accordance with the Commercial Software License Agreement provided with the Software or, alternatively, in accordance with the terms contained in a written agreement between you and Sencha.

If you are unsure which license is appropriate for your use, please contact the sales department at http://www.sencha.com/contact.

*/
/**
 * @author Ed Spencer
 * @class Ext.ModelManager
 * @extends Ext.AbstractManager

The ModelManager keeps track of all {@link Ext.data.Model} types defined in your application.

__Creating Model Instances__
Model instances can be created by using the {@link #create} function. It is also possible to do
this by using the Model type directly. The following snippets are equivalent:

    Ext.define('User', {
        extend: 'Ext.data.Model',
        fields: ['first', 'last']
    });
    
    // method 1, create through the manager
    Ext.ModelManager.create({
        first: 'Ed',
        last: 'Spencer'
    }, 'User');
    
    // method 2, create on the type directly
    new User({
        first: 'Ed',
        last: 'Spencer'
    });
    
__Accessing Model Types__
A reference to a Model type can be obtained by using the {@link #getModel} function. Since models types
are normal classes, you can access the type directly. The following snippets are equivalent:

    Ext.define('User', {
        extend: 'Ext.data.Model',
        fields: ['first', 'last']
    });
    
    // method 1, access model type through the manager
    var UserType = Ext.ModelManager.getModel('User');
    
    // method 2, reference the type directly
    var UserType = User;

 * @markdown
 * @singleton
 */
Ext.define('Ext.ModelManager', {
    extend: 'Ext.AbstractManager',
    alternateClassName: 'Ext.ModelMgr',
    requires: ['Ext.data.Association'],
    
    singleton: true,
    
    typeName: 'mtype',
    
    /**
     * Private stack of associations that must be created once their associated model has been defined
     * @property associationStack
     * @type Array
     */
    associationStack: [],
    
    /**
     * Registers a model definition. All model plugins marked with isDefault: true are bootstrapped
     * immediately, as are any addition plugins defined in the model config.
     * @private
     */
    registerType: function(name, config) {
        var proto = config.prototype,
            model;
        if (proto && proto.isModel) {
            // registering an already defined model
            model = config;
        } else {
            // passing in a configuration
            if (!config.extend) {
                config.extend = 'Ext.data.Model';
            }
            model = Ext.define(name, config);
        }
        this.types[name] = model;
        return model;
    },
    
    /**
     * @private
     * Private callback called whenever a model has just been defined. This sets up any associations
     * that were waiting for the given model to be defined
     * @param {Function} model The model that was just created
     */
    onModelDefined: function(model) {
        var stack  = this.associationStack,
            length = stack.length,
            create = [],
            association, i, created;
        
        for (i = 0; i < length; i++) {
            association = stack[i];
            
            if (association.associatedModel == model.modelName) {
                create.push(association);
            }
        }
        
        for (i = 0, length = create.length; i < length; i++) {
            created = create[i];
            this.types[created.ownerModel].prototype.associations.add(Ext.data.Association.create(created));
            Ext.Array.remove(stack, created);
        }
    },
    
    /**
     * Registers an association where one of the models defined doesn't exist yet.
     * The ModelManager will check when new models are registered if it can link them
     * together
     * @private
     * @param {Ext.data.Association} association The association
     */
    registerDeferredAssociation: function(association){
        this.associationStack.push(association);
    },
    
    /**
     * Returns the {@link Ext.data.Model} for a given model name
     * @param {String/Object} id The id of the model or the model instance.
     */
    getModel: function(id) {
        var model = id;
        if (typeof model == 'string') {
            model = this.types[model];
        }
        return model;
    },
    
    /**
     * Creates a new instance of a Model using the given data.
     * @param {Object} data Data to initialize the Model's fields with
     * @param {String} name The name of the model to create
     * @param {Number} id Optional unique id of the Model instance (see {@link Ext.data.Model})
     */
    create: function(config, name, id) {
        var con = typeof name == 'function' ? name : this.types[name || config.name];
        
        return new con(config, id);
    }
}, function() {
    
    /**
     * Creates a new Model class from the specified config object. See {@link Ext.data.Model} for full examples.
     * 
     * @param {Object} config A configuration object for the Model you wish to create.
     * @return {Ext.data.Model} The newly registered Model
     * @member Ext
     * @method regModel
     */
    Ext.regModel = function() {
        //<debug>
        if (Ext.isDefined(Ext.global.console)) {
            Ext.global.console.warn('Ext.regModel has been deprecated. Models can now be created by extending Ext.data.Model: Ext.define("MyModel", {extend: "Ext.data.Model", fields: []});.');
        }
        //</debug>
        return this.ModelManager.registerType.apply(this.ModelManager, arguments);
    };
});

